/**
 * 
 */
package common;

import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.mail.internet.AddressException;
import javax.mail.internet.InternetAddress;

import sun.misc.BASE64Encoder;
import data.Pagination;

/**
 * Common Utility.
 * 
 * @author chuxiaoyuan
 * 
 */
public final class Utils {

	/**
	 * No instance.
	 */
	private Utils() {
		//
	}

	/**
	 * The logger.
	 */
	private static final Logger logger = new Logger(Utils.class);

	/**
	 * Cast number to narrow types.
	 * 
	 * @param castType
	 *            the type to cast to (a subclass of java.lang.Number).
	 * @param value
	 *            the value of the number.
	 * @return the casted result.
	 */
	public static Number NarrowNumberCast(Class<?> castType, Object value) {

		if (value == null) {
			return null;
		}

		// Validate input
		if (!(value instanceof Number)) {
			throw new ClassCastException("NarrowNumberCast cannnot cast from "
					+ value.getClass());
		}
		Number in = (Number) value;
		Number result = null;

		// Doing cast.
		if (Byte.class.equals(castType)) {
			result = in.byteValue();

		} else if (Short.class.equals(castType)) {
			result = in.shortValue();

		} else if (Integer.class.equals(castType)) {
			result = in.intValue();

		} else if (Long.class.equals(castType)) {
			result = in.longValue();

		} else if (Float.class.equals(castType)) {
			result = in.floatValue();

		} else if (Double.class.equals(castType)) {
			result = in.doubleValue();

		} else {
			throw new ClassCastException("NarrowNumberCast cannnot cast to "
					+ castType);
		}
		return result;
	}

	/**
	 * Encrypt plain text password.
	 * 
	 * @param plainText
	 *            the plain text password.
	 * @return the encrypted password.
	 * @throws NoSuchAlgorithmException
	 *             Invalid encryption algorithm.
	 * @throws UnsupportedEncodingException
	 *             Encryption failed.
	 */
	public static String encryptPassword(String plainText)
			throws NoSuchAlgorithmException, UnsupportedEncodingException {

		if (plainText == null || plainText.length() == 0) {
			return null;
		}

		// Encrypt password
		MessageDigest md;
		String algorithm = "SHA";
		try {
			md = MessageDigest.getInstance(algorithm);

		} catch (NoSuchAlgorithmException e) {

			String msg = "Invalid encryption algorithm: " + algorithm;
			logger.log(msg, e);
			throw e;
		}

		try {
			// Do encryption.
			md.update(plainText.getBytes("UTF-8"));
			byte[] encrypted = md.digest();
			return (new BASE64Encoder()).encode(encrypted);

		} catch (UnsupportedEncodingException e) {

			String msg = "Encrypt password failed: " + algorithm;
			logger.log(msg, e);
			throw e;
		}
	}

	/**
	 * Validate email address.
	 * 
	 * @param email
	 *            email address.
	 * @return true if it is a valid email.
	 */
	public static boolean validateEmailAddress(String email) {
		try {
			
			// Validate email address.
			InternetAddress emailAddr = new InternetAddress(email);
			emailAddr.validate();
			return true;

		} catch (AddressException e) {
			return false;
		}
	}

	/**
	 * Validate password.
	 * 
	 * @param password
	 *            the password.
	 * @return true if it is a valid password.
	 */
	public static boolean validatePassword(String password) {

		Pattern pattern = Pattern
				.compile("((?=.*\\d)(?=.*[a-z])(?=.*[A-Z]).{6,20})");
		Matcher matcher = pattern.matcher(password);
		return matcher.matches();
	}
	
	/**
	 * The default pagination size (number of records per page).
	 */
	private static final int DEFAULT_PAGINATION_SIZE = 15;
	
	/**
	 * Get the pagination with default page size.
	 * 
	 * @param pageNo the page No.
	 * 
	 * @return the pagination.
	 */
	public static Pagination getPagination(int pageNo) {
		return new Pagination(DEFAULT_PAGINATION_SIZE, pageNo);
	}
	
	/**
	 * Get the pagination with the specified page size and page No.
	 * 
	 * @param pageSize the page size.
	 * @param pageNo the page No.
	 * 
	 * @return the pagination.
	 */
	public static Pagination getPagination(int pageSize, int pageNo) {
		return new Pagination(pageSize, pageNo);
	}
}
